home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Java Programmer's Toolkit
/
Java Programmer's Toolkit.iso
/
applets
/
collectn
/
checkedc.jav
< prev
next >
Wrap
Text File
|
1995-12-14
|
16KB
|
551 lines
/*
File: CheckedCollection.java
Originally written by Doug Lea and released into the public domain.
Thanks for the assistance and support of Sun Microsystems Labs, Agorics
Inc, Loral, and everyone contributing, testing, and using this code.
History:
Date Who What
24Sep95 dl@cs.oswego.edu Create from collections.java working file
13Oct95 dl Misc clean-up.
19Oct95 dl more misc clean-up.
*/
package collections;
import java.util.Enumeration;
import java.util.NoSuchElementException;
/**
*
* Base class for Collection testing.
* CheckCollections are simple harnesses around other Collections.
* They include constructors that accept any other kind of collection, c.
* They mirror each operation in the corresponding interface.
* For each operation, they invoke c.checkImplementation(),
* then operate upon c,
* then check for as many testable effects of the operation
* as is feasible, and then recheck c.
* <P>
* This is not usually very fast. Among other reasons,
* checks often require that copies of state be made to compare against later.
* <P>
* @author Doug Lea
* @version 0.94
*
* <P> For an introduction to this package see <A HREF="index.html"> Overview </A>.
**/
public class CheckedCollection implements UpdatableCollection {
/**
* The collection performing the actual work.
* (All instance variables are public so can be inspected easily in tests)
**/
public UpdatableCollection thys;
/**
* A clone of the thys made before an operation
**/
public UpdatableCollection prev;
/**
* The version number of thys before an operation
**/
public int prevVersion;
/**
* Wrap collection c in inside a Checker
**/
public CheckedCollection(UpdatableCollection c) {
thys = c;
thys.checkImplementation();
}
/**
* Make a Checked clone of underlying collection
**/
protected Object clone() throws CloneNotSupportedException {
return new CheckedCollection((UpdatableCollection)(thys.duplicate()));
}
/**
* Wrapper for clone()
* @see clone
**/
public synchronized Collection duplicate() {
Collection c = null;
try {
c = (Collection)(this.clone());
} catch (CloneNotSupportedException ex) {}
return c;
}
/**
* Call at the end of any checked method
**/
protected void postCheck() {
thys.checkImplementation();
prev = null; // side effect -- detach prev so GC can claim it
}
/**
* Call at the beginnning of any checked method requiring
* use of prev clone
**/
protected void preCheck() {
prev = (UpdatableCollection)(thys.duplicate());
prevVersion = thys.version();
}
/**
* Checks collections.Collection.canInclude according to its specification
* @see collections.Collection#canInclude
**/
public synchronized boolean canInclude(Object element) {
preCheck();
boolean result = thys.canInclude(element);
// not allowed to report true for null
assert(!(result == true && element == null));
// must be side-effect free
assert(thys.sameStructure(prev));
postCheck();
return result;
}
/**
* Checks collections.Collection.isEmpty according to its specification
* @see collections.Collection#isEmpty
**/
public synchronized boolean isEmpty() {
preCheck();
boolean result = thys.isEmpty();
// must be behaviorally equivalent to asking if size() == 0
assert(result == (thys.size() == 0));
assert(thys.sameStructure(prev));
postCheck();
return result;
}
/**
* Checks collections.Collection.size according to its specification
* @see collections.Collection#size
**/
public synchronized int size() {
preCheck();
int result = thys.size();
// sizes are non-negative
assert(result >= 0);
// cross-check of isEmpty assertion
assert( (result == 0) == thys.isEmpty());
assert(thys.sameStructure(prev));
postCheck();
return result;
}
/**
* Checks collections.Collection.occurrencesOf according to its specification
* @see collections.Collection#occurrencesOf
**/
public synchronized int occurrencesOf(Object element) {
preCheck();
int result = thys.occurrencesOf(element);
// occurrences are non-negative
assert((result >= 0));
// cannot be greater than size()
assert(result <= thys.size());
// occurrences of things you canot include must be 0
assert(thys.canInclude(element) || result == 0);
// cross-check of include condition
assert( !((result == 0) && thys.includes(element)));
assert(thys.sameStructure(prev));
postCheck();
return result;
}
/**
* Checks collections.Collection.includes according to its specification
* @see collections.Collection#includes
**/
public synchronized boolean includes(Object element) {
preCheck();
boolean result = thys.includes(element);
// must be equiv to asking if occurrences >= 1
assert(result == (thys.occurrencesOf(element) >= 1));
assert(thys.sameStructure(prev));
postCheck();
return result;
}
/**
* Checks collections.Collection.elements according to its specification
* @see collections.Collection#elements
**/
public synchronized CollectionEnumeration elements() {
// get one and make sure it is OK
int c = thys.size();
CollectionEnumeration e = thys.elements();
while (e.hasMoreElements()) {
// reports correct number of elements
assert(e.numberOfRemainingElements() == c);
--c;
Object v = e.nextElement();
// each element reported actually occurs
assert(thys.includes(v));
}
// We've exhausted e; return another one
postCheck();
return thys.elements();
}
/**
* Checks collections.Collection.sameStructure according to its specification
* @see collections.Collection#sameStructure
**/
public synchronized boolean sameStructure(Collection c) {
// cannot check since we rely on it to check other assertions
boolean result = thys.sameStructure(c);
postCheck();
return result;
}
/**
* Implements Object.toString
**/
public String toString() {
// cannot check
String result = thys.toString();
postCheck();
return result;
}
/**
* Checks collections.UpdatableCollection.version according to its specification
* @see collections.UpdatableCollection#version
**/
public synchronized int version() {
// nothing to check
int result = thys.version();
postCheck();
return result;
}
/**
* Checks collections.UpdatableCollection.clear according to its specification
* @see collections.UpdatableCollection#clear
**/
public synchronized void clear() {
preCheck();
thys.clear();
// mst now be empty
assert(thys.isEmpty());
// version change only if not previously empty
assert((thys.version() == prevVersion) == prev.isEmpty());
postCheck();
}
/**
* Checks collections.UpdatableCollection.exclude according to its specification
* @see collections.UpdatableCollection#exclude
**/
public synchronized void exclude(Object element) {
preCheck();
thys.exclude(element);
checkRemove(thys, prev, element, true, true);
postCheck();
}
/**
* Checks collections.Collection.excluding.
* @see collections.Collection#excluding
**/
public synchronized Collection excluding(Object element) {
preCheck();
Collection nc = thys.excluding(element);
checkRemove(nc, thys, element, true, false);
assert(thys.sameStructure(prev));
postCheck();
return nc;
}
/**
* Checks collections.UpdatableCollection.removeOneOf according to its specification
* @see collections.UpdatableCollection#removeOneOf
**/
public synchronized void removeOneOf(Object element) {
preCheck();
thys.removeOneOf(element);
checkRemove(thys, prev, element, false, true);
postCheck();
}
/**
* Checks collections.Collection.removingOneOf
* @see collections.Collection#removingOneOf
**/
public synchronized Collection removingOneOf(Object element) {
preCheck();
Collection nc = thys.removingOneOf(element);
checkRemove(nc, thys, element, false, false);
assert(thys.sameStructure(prev));
postCheck();
return nc;
}
/**
* Checks collections.UpdatableCollection.replaceOneOf according to its specification
* @see collections.UpdatableCollection#replaceOneOf
**/
public synchronized void replaceOneOf(Object oldElement, Object newElement)
throws IllegalElementException {
preCheck();
try {
thys.replaceOneOf(oldElement, newElement);
checkReplace(thys, prev, oldElement, newElement, false, true);
postCheck();
}
catch(IllegalElementException ex) {
assert(thys.includes(oldElement) && !thys.canInclude(newElement));
assert(thys.sameStructure(prev));
postCheck();
throw ex;
}
}
/**
* Checks collections.Collection.replacingOneOf
* @see collections.Collection#replacingOneOf
**/
public synchronized Collection replacingOneOf(Object oldElement,
Object newElement)
throws IllegalElementException {
preCheck();
try {
Collection nc = thys.replacingOneOf(oldElement, newElement);
checkReplace(nc, thys, oldElement, newElement, false, false);
assert(thys.sameStructure(prev));
postCheck();
return nc;
}
catch(IllegalElementException ex) {
assert(thys.includes(oldElement) && !thys.canInclude(newElement));
assert(thys.sameStructure(prev));
postCheck();
throw ex;
}
}
/**
* Checks collections.UpdatableCollection.replaceAllOf according to its specification
* @see collections.UpdatableCollection#replaceAllOf
**/
public synchronized void replaceAllOf(Object oldElement, Object newElement)
throws IllegalElementException {
preCheck();
try {
thys.replaceAllOf(oldElement, newElement);
checkReplace(thys, prev, oldElement, newElement, true, true);
postCheck();
}
catch(IllegalElementException ex) {
assert(thys.includes(oldElement) && !thys.canInclude(newElement));
assert(thys.sameStructure(prev));
postCheck();
throw ex;
}
}
/**
* Checks collections.Collection.replacingAllOf
* @see collections.Collection#replacingAllOf
**/
public synchronized Collection replacingAllOf(Object oldElement,
Object newElement)
throws IllegalElementException {
preCheck();
try {
Collection nc = thys.replacingAllOf(oldElement, newElement);
checkReplace(nc, thys, oldElement, newElement, true, false);
assert(thys.sameStructure(prev));
postCheck();
return nc;
}
catch(IllegalElementException ex) {
assert(thys.includes(oldElement) && !thys.canInclude(newElement));
assert(thys.sameStructure(prev));
postCheck();
throw ex;
}
}
/**
* Checks collections.UpdatableCollection.take according to its specification
* @see collections.UpdatableCollection#take
**/
public Object take() throws NoSuchElementException {
preCheck();
try {
Object result = thys.take();
assert(result != null);
checkRemove(thys, prev, result, false, true);
postCheck();
return result;
}
catch (NoSuchElementException ex) {
assert(prev.isEmpty());
assert(thys.sameStructure(prev));
postCheck();
throw ex;
}
}
/**
* Checks collections.UpdatableCollection.excludeElements
* @see collections.UpdatableCollection#excludeElements
**/
public synchronized void excludeElements(Enumeration e)
throws CorruptedEnumerationException {
preCheck();
thys.excludeElements(e);
// can't check elements since e has been exhausted --
// we have no handle to them.
postCheck();
}
/**
* Checks collections.UpdatableCollection.removeElements
* @see collections.UpdatableCollection#removeElements
**/
public synchronized void removeElements(Enumeration e)
throws CorruptedEnumerationException {
preCheck();
thys.removeElements(e);
// Same problem as above
postCheck();
}
/**
* Implements collections.ImplementationCheckable.assert.
* @see collections.ImplementationCheckable#assert
**/
public void assert(boolean pred)
throws ImplementationError {
ImplementationError.assert(thys, pred);
}
/**
* Implements collections.ImplementationCheckable.checkImplementation
* @see collections.ImplementationCheckable#checkImplementation
**/
public synchronized void checkImplementation()
throws ImplementationError {
// we cannot check ourselves!
}
/**
* Helper for checking remov*, exclud*, take
**/
protected void checkRemove(Collection nc, Collection oc,
Object element,
boolean allOccurrences, boolean verchk) {
int prevOcc = oc.occurrencesOf(element);
int reqOcc = 0;
if (!allOccurrences && prevOcc > 1)
reqOcc = prevOcc - 1;
int newOcc = nc.occurrencesOf(element);
assert(newOcc == reqOcc);
// size reflects removals
assert(nc.size() == oc.size() - (prevOcc - newOcc));
if (verchk) {
// version change only if occurrences change
int nv = ((UpdatableCollection)nc).version();
assert((nv == prevVersion) == (prevOcc == newOcc));
}
// all other elements the same
CollectionEnumeration os = oc.elements();
while (os.hasMoreElements()) {
Object v = os.nextElement();
assert(!v.equals(element) ==
(nc.occurrencesOf(v) == oc.occurrencesOf(v)));
}
nc.checkImplementation();
}
/**
* Helper for checking replac*
**/
protected void checkReplace(Collection nc, Collection oc,
Object oldElement, Object newElement,
boolean allOccurrences, boolean verchk) {
int delta = prev.occurrencesOf(oldElement);
// for OneOf, occurrences of old element == max(0, old occurrences - 1)
if (!allOccurrences && delta > 1) delta = 1;
// ... unless they are equal, in which case unchanged
if (oldElement.equals(newElement)) delta = 0;
assert(nc.occurrencesOf(oldElement) ==
oc.occurrencesOf(oldElement) - delta);
if (delta != 0)
assert(nc.canInclude(newElement));
// new occurrences added by one
int reqNewOcc = prev.occurrencesOf(newElement) + delta;
// .. unless it obeys Set semantics, in which case max of 1
if (reqNewOcc > 1 && (nc instanceof Set)) reqNewOcc = 1;
assert(nc.occurrencesOf(newElement) == reqNewOcc);
if (verchk) {
// version change only if occurrences change
int nv = ((UpdatableCollection)nc).version();
assert(!((nv == prevVersion) && (delta != 0)));;
}
// all other elements the same
CollectionEnumeration os = oc.elements();
while (os.hasMoreElements()) {
Object v = os.nextElement();
if (!v.equals(oldElement) && !v.equals(newElement))
assert(nc.occurrencesOf(v) == oc.occurrencesOf(v));
}
nc.checkImplementation();
}
}